package com.xydpeng.rpc.serialize.support.protostuff;

import com.xydpeng.rpc.serialize.Serialization;
import io.protostuff.LinkedBuffer;
import io.protostuff.ProtostuffIOUtil;
import io.protostuff.Schema;
import io.protostuff.runtime.RuntimeSchema;

import java.io.InputStream;


/**
 * @author xydpeng
 * @version v1.0
 * @date: 2021/7/2
 * @description:
 */
public class ProtostuffSerialization implements Serialization {

    private static final ProtostuffSerialization instance = new ProtostuffSerialization();

    public static ProtostuffSerialization getInstance() {
        return instance;
    }


    @Override
    public <T> byte[] serialize(T obj) {
        Class<T> clazz = (Class<T>) obj.getClass();
        // this is lazily created and cached by RuntimeSchema
        // so its safe to call RuntimeSchema.getSchema(Foo.class) over and over
        // The getSchema method is also thread-safe
        Schema<T> schema = RuntimeSchema.getSchema(clazz);
        // Re-use (manage) this buffer to avoid allocating on every serialization
        LinkedBuffer buffer = LinkedBuffer.allocate(512);
        // ser
        final byte[] protostuff;
        try {
            protostuff = ProtostuffIOUtil.toByteArray(obj, schema, buffer);
        } catch (Exception e) {
            throw new RuntimeException("serialize " + clazz + " exception:", e);
        } finally {
            buffer.clear();
        }
        return protostuff;
    }

    @Override
    public <T> T deserialize(byte[] bytes, Class<T> clazz) {
        if (bytes == null || bytes.length == 0) {
            return null;
        }
        try {
            // this is lazily created and cached by RuntimeSchema
            // so its safe to call RuntimeSchema.getSchema(Foo.class) over and over
            // The getSchema method is also thread-safe
            Schema<T> schema = RuntimeSchema.getSchema(clazz);
            T instance = schema.newMessage();
            ProtostuffIOUtil.mergeFrom(bytes, instance, schema);
            return instance;
        } catch (Exception e) {
            throw new RuntimeException("deserialize " + clazz + " exception:", e);
        }
    }

    public <T> T deserialize(InputStream in, Class<T> clazz) {
        if (in == null) {
            throw new IllegalArgumentException("No inputStream specified");
        }
        try {
            // this is lazily created and cached by RuntimeSchema
            // so its safe to call RuntimeSchema.getSchema(Foo.class) over and over
            // The getSchema method is also thread-safe
            Schema<T> schema = RuntimeSchema.getSchema(clazz);
            T instance = schema.newMessage();
            ProtostuffIOUtil.mergeFrom(in, instance, schema);
            return instance;
        } catch (Exception e) {
            throw new RuntimeException("deserialize " + clazz + " exception:", e);
        }
    }


}
